iT邦幫忙

2023 iThome 鐵人賽

DAY 10
1

今天我們來寫code操作前幾天設定好的AWS SSM,SSM可以用來安全的儲存一些機密的參數,我們在部屬上AWS Lambda後,像是Line的Access Token和Secret這類參數都會存放在裡面,而本機端開發方便的存放在環境變數裡就好。

那接著我們就一步步來寫操作SSM的function以及對應的Unit Test吧~

  1. 一樣我們先開一個資料夾專門處理ssm,並且建立ssm.go以及對應的ssm_test.go,還有一個config.json用來mock參數的
    https://ithelp.ithome.com.tw/upload/images/20230925/20115990yX1Lckfmkp.png

  2. config.json,填入我們可能會存的參數名稱,這邊以Secret和AccessToken為例

    {
        "MockChannelSecret":"secret-name",
        "MockChannelAccessToken":"token-name"
    }
    
  3. 接著ssm.go,我們一樣建立一個SSM的結構,會存放一個操作SSM的Client

    type SSM struct {
        Client *ssm.Client
    }
    
    func NewSSM() *SSM {
        cfg, err := config.LoadDefaultConfig(context.TODO())
        if err != nil {
            panic(err)
        }
    
        client := ssm.NewFromConfig(cfg)
        return &SSM{
            Client: client,
        }
    }
    
    
  4. 這邊我們定義一個interface SSMGetParameterAPI,並且它裡面定義了GetParameter的方法,GetParameter是用來從SSM拿取參數的方法,我們將他定義在interface裡面的話,可以在測試的時候使用SSMGetParameterAPI模擬AWS取SSM參數的動作。

    type SSMGetParameterAPI interface {
        GetParameter(ctx context.Context,
            params *ssm.GetParameterInput,
            optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)
    }
    
    func (s *SSM) FindParameter(c context.Context, api SSMGetParameterAPI, name string) (string, error) {
        input := &ssm.GetParameterInput{
            Name: &name,
        }
        results, err := api.GetParameter(c, input)
        if err != nil {
            fmt.Println(err.Error())
            return "", err
        }
        fmt.Println(*results.Parameter.Value)
    
        return *results.Parameter.Value, nil
    }
    
  5. 完整的ssm.go檔案如下

    package ssm
    
    import (
        "context"
        "fmt"
    
        "github.com/aws/aws-sdk-go-v2/config"
        "github.com/aws/aws-sdk-go-v2/service/ssm"
    )
    
    type SSMGetParameterAPI interface {
        GetParameter(ctx context.Context,
            params *ssm.GetParameterInput,
            optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)
    }
    
    func (s *SSM) FindParameter(c context.Context, api SSMGetParameterAPI, name string) (string, error) {
        input := &ssm.GetParameterInput{
            Name: &name,
        }
        results, err := api.GetParameter(c, input)
        if err != nil {
            fmt.Println(err.Error())
            return "", err
        }
        fmt.Println(*results.Parameter.Value)
    
        return *results.Parameter.Value, nil
    }
    
    type SSM struct {
        Client *ssm.Client
    }
    
    func NewSSM() *SSM {
        cfg, err := config.LoadDefaultConfig(context.TODO())
        if err != nil {
            panic(err)
        }
    
        client := ssm.NewFromConfig(cfg)
        return &SSM{
            Client: client,
        }
    }
    
    
  6. 接著打開ssm_test.go,寫好TestMain,New一個共用的testSSM

    var testSSM *SSM
    
    func TestMain(m *testing.M) {
    
        testSSM = NewSSM()
    
        os.Exit(m.Run())
    }
    
    
  7. 我們先建立一個struct來implement剛剛的SSMGetParameterAPI,我們自己實現一個GetParameter,讓他依照讀到的參數名稱傳出對應的Parameter Value,來模擬AWS取得SSM參數的功能。

    type SSMGetParameterImpl struct{}
    
    func (dt SSMGetParameterImpl) GetParameter(ctx context.Context,
        params *ssm.GetParameterInput,
        optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) {
    
        var parameter *types.Parameter
    
        if *params.Name == "secret-name" {
            parameter = &types.Parameter{Value: aws.String("secret-value")}
        }
        if *params.Name == "token-name" {
            parameter = &types.Parameter{Value: aws.String("token-value")}
        }
    
        output := &ssm.GetParameterOutput{
            Parameter: parameter,
        }
    
        return output, nil
    }
    
  8. 解析config.json,取出json存放對應的參數名稱

    type Config struct {
        MockChannelSecret      string `json:"MockChannelSecret"`
        MockChannelAccessToken string `json:"MockChannelAccessToken"`
    }
    
    var configFileName = "config.json"
    
    var globalConfig Config
    
    func populateConfiguration(t *testing.T) error {
        content, err := os.ReadFile(configFileName)
        if err != nil {
            return err
        }
    
        text := string(content)
    
        err = json.Unmarshal([]byte(text), &globalConfig)
        if err != nil {
            return err
        }
    
        if globalConfig.MockChannelSecret == "" {
            msg := "You must supply a value for MockChannelSecret in " + configFileName
            return errors.New(msg)
        }
        if globalConfig.MockChannelAccessToken == "" {
            msg := "You must supply a value for MockChannelAccessToken in " + configFileName
            return errors.New(msg)
        }
    
        return nil
    }
    
  9. 補上測試,實際去調用FindParameter,由於我們放進FindParameter的是api := &SSMGetParameterImpl{},而他的GetParameter已經被我們替換掉了,所以就能取出我們在GetParameter預設要存放的值瞜~

    func TestFindParameter(t *testing.T) {
        thisTime := time.Now()
        nowString := thisTime.Format("2006-01-02 15:04:05 Monday")
        t.Log("Starting unit test at " + nowString)
    
        err := populateConfiguration(t)
        if err != nil {
            t.Fatal(err)
        }
    
        api := &SSMGetParameterImpl{}
    
        respSecret, err := testSSM.FindParameter(context.Background(), *api, globalConfig.MockChannelSecret)
        if err != nil {
            t.Log("Got an error ...:")
            t.Log(err)
            return
        }
    
        t.Log("MockChannelSecret value: " + respSecret)
    
        respToken, err := testSSM.FindParameter(context.Background(), *api, globalConfig.MockChannelAccessToken)
        if err != nil {
            t.Log("Got an error ...:")
            t.Log(err)
            return
        }
    
        t.Log("MockChannelAccessToken value: " + respToken)
    }
    
  10. 最後測試跑過就會像下面這樣囉~

    https://ithelp.ithome.com.tw/upload/images/20230925/20115990cYTU9nV0iQ.png


上一篇
Day09 串接DynamoDB-02
下一篇
Day11 Sever"啟動" - Gin + ( Lambda or Ngrok ) 01
系列文
Golang LineBot X GoogleDrive:LINE有各種限制!? 那就丟上Drive吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言